package com.ht.modules.system.controller;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ht.entity.*;
import com.ht.modules.system.entity.CommonConfigVo;
import com.ht.modules.system.entity.SysDepart;
import com.ht.modules.system.entity.SysUser;
import com.ht.modules.system.service.*;
import com.ht.modules.system.shiro.vo.DefContants;
import com.ht.api.ISysBaseAPI;
import com.ht.constant.CommonConstant;
import com.ht.modules.system.model.SysLoginModel;
import com.ht.utils.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

/**
 *
 * @since 2018-12-17
 */
@RestController
@RequestMapping("/sys")
@Api(tags = "AA置顶-用户登录")
@Slf4j
public class LoginController {

    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysBaseAPI sysBaseAPI;
    @Autowired
    private ISysLogService logService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ISysDepartService sysDepartService;
    @Autowired
    private ISysUserRoleService sysUserRoleService;
    @Autowired
    private ISysLoginErrorService sysLoginErrorService;
    @Autowired
    private ICommonConfigService commonConfigService;

    @PostMapping(value = "/login")
    @ApiOperation("登录接口")
    public Result<JSONObject> login(@RequestBody SysLoginModel sysLoginModel, HttpServletRequest request) {
        Result<JSONObject> result = new Result<>();
        String username = sysLoginModel.getUsername();
        String password = sysLoginModel.getPassword();
        SysUser sysUser = sysUserService.getUserByName(username);
        if (sysUser == null) {
            result.error500("该用户不存在");
            sysBaseAPI.addLog("登录失败，用户名:" + username + "不存在！",
                    CommonConstant.LOG_TYPE_1, null);
            return result;
        }
        // 验证账号状态
        if (sysUser.getStatus().equals(DicConstants.SYSUSER_STATUS_FROZEN)) {
            result.error500("您的账号已被冻结，请联系管理员解除！");
            return result;
        }
        // 获取用户ip
        String ipAddress;
        try {
            ipAddress = IPUtils.getIpAddr(request);
        } catch (Exception e) {
            ipAddress = "127.0.0.1";
        }
        // 判断该ip下该用户是否连续输错五次密码
        String redisKey = DicConstants.LOGIN_ERROR_TIME + ipAddress + username;
        Integer oldErrorTimes = (Integer) redisUtil.get(redisKey);
        if (oldErrorTimes != null && oldErrorTimes >= 5) {
            result.error500("该用户于该设备密码错误次数已达当日上限！");
            return result;
        }
        // 密码验证
        String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
        String syspassword = sysUser.getPassword();
        // 验证密码是否正确
        if (!syspassword.equals(userpassword)) {
            int currentErrorTimes;
            if (oldErrorTimes == null) {
                // 如果之前没有记录，则在redis中标记为1
                currentErrorTimes = 1;
            } else {
                // 如果之前有记录，则加1
                currentErrorTimes = oldErrorTimes + 1;
            }
            redisUtil.set(redisKey, currentErrorTimes);
            // 设置超时时间（暂定24小时）
            // 修改值时，需要重新设置超时时间，否则会自动变为无限存活
            redisUtil.expire(redisKey, 24 * 60 * 60);
            // 为防止登录错误表中存大量无用数据，所以只存第五次输错密码
            if (currentErrorTimes == 5) {
                sysLoginErrorService.add(ipAddress, username);
            }
            result.error500("密码错误！连续输入错误5次账号将被锁定24小时，当前第" + currentErrorTimes + "次！");
            return result;
        }
        // 如果用户成功登录并且有登录异常记录，则清除
        if (oldErrorTimes != null) {
            redisUtil.del(redisKey);
        }
        String cid = sysLoginModel.getCid();
        //退出之前的登录账号
        //当从系统配置里获取启用单设备登录时执行退出通账号的其他登录
        //当cid存在时判断为APP登录的，需保证同时有一个APP和pc同时在线
        CommonConfigVo uniqueConfig = commonConfigService.getUniqueConfig();
        if ("yes".equals(uniqueConfig.getEnableSingleDeviceLogin())) {
            if (Strings.isNotEmpty(cid)) {
                redisUtil.delete(CommonConstant.PREFIX_USER_TOKEN_APP + username + CommonConstant.PREFIX_USER_TOKEN_CONNECTION + "*");
                redisUtil.del(CommonConstant.LOGIN_USER_CACHE_RULES_ROLE + username);
                redisUtil.del(CommonConstant.LOGIN_USER_CACHE_RULES_PERMISSION + username);
            } else {
                redisUtil.delete(CommonConstant.PREFIX_USER_TOKEN_PC + username + CommonConstant.PREFIX_USER_TOKEN_CONNECTION + "*");
                redisUtil.del(CommonConstant.LOGIN_USER_CACHE_RULES_ROLE + username);
                redisUtil.del(CommonConstant.LOGIN_USER_CACHE_RULES_PERMISSION + username);
            }

        }

//        Object object = redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token);

        String token = null;
        //生成token
        if (Strings.isNotEmpty(cid)) {
            token = JwtUtil.signAPP(username, syspassword);
            redisUtil.set(CommonConstant.PREFIX_USER_TOKEN_APP + username + CommonConstant.PREFIX_USER_TOKEN_CONNECTION + token, token);
            //设置超时时间
            redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN_APP + username + CommonConstant.PREFIX_USER_TOKEN_CONNECTION + token, JwtUtil.EXPIRE_TIME_APP / 1000);

        } else {
            token = JwtUtil.signPC(username, syspassword);
            redisUtil.set(CommonConstant.PREFIX_USER_TOKEN_PC + username + CommonConstant.PREFIX_USER_TOKEN_CONNECTION + token, token);
            //设置超时时间
            redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN_PC + username + CommonConstant.PREFIX_USER_TOKEN_CONNECTION + token, JwtUtil.EXPIRE_TIME_PC / 1000);

        }

        // 更新cid

        if (Strings.isNotEmpty(cid) && !cid.equals(sysUser.getCid())) {
            sysUser.setCid(cid);
            sysUserService.saveOrUpdate(sysUser);
        }

        // 获取用户id
        String sysUserId = sysUser.getId();
        //获取用户部门信息
        JSONObject obj = new JSONObject();
        List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUserId);
        // 获取用户所属企业
        List<SysDepart> companyList = sysDepartService.queryUserFirstDeparts(sysUserId);
        obj.put("companyList", companyList);
        obj.put("departs", departs);
        if (departs == null || departs.isEmpty()) {
            obj.put("multi_depart", 0);
        } else if (departs.size() == 1) {
            sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
            obj.put("multi_depart", 1);
        } else {
            obj.put("multi_depart", 2);
        }
        // 获取用户角色
        List<String> roleCode = sysUserRoleService.queryRoleCodeByUserId(sysUserId);
        obj.put("token", token);
        obj.put("userInfo", sysUser);
        obj.put("roleCode", roleCode);
        result.setResult(obj);
        result.success("登录成功");
        sysBaseAPI.addLog("用户名: " + username + ",登录成功！",
                CommonConstant.LOG_TYPE_1, null);
        return result;
    }

    /**
     * 退出登录
     */
    @RequestMapping(value = "/logout")
    public Result<Object> logout(HttpServletRequest request) {
        //用户退出逻辑
        Subject subject = SecurityUtils.getSubject();
        LoginUser sysUser = (LoginUser) subject.getPrincipal();
        String realname = sysUser.getRealname();
        String username = sysUser.getUsername();
        sysBaseAPI.addLog("用户名: " + realname + ",退出成功！",
                CommonConstant.LOG_TYPE_1, null);
        log.info(" 用户名:  " + realname + ",退出成功！ ");
        subject.logout();

        String token = request.getHeader(DefContants.X_ACCESS_TOKEN);
        //清空用户Token缓存
        redisUtil.del(CommonConstant.PREFIX_USER_TOKEN_PC + username + CommonConstant.PREFIX_USER_TOKEN_CONNECTION + token);
        redisUtil.del(CommonConstant.PREFIX_USER_TOKEN_APP + username + CommonConstant.PREFIX_USER_TOKEN_CONNECTION + token);
        //清空用户权限缓存：权限Perms和角色集合
        redisUtil.del(CommonConstant.LOGIN_USER_CACHE_RULES_ROLE + username);
        redisUtil.del(CommonConstant.LOGIN_USER_CACHE_RULES_PERMISSION + username);
        return Result.ok("退出登录成功！");
    }

    /**
     * 获取访问量
     */
    @GetMapping("loginfo")
    public Result<JSONObject> loginfo() {
        Result<JSONObject> result = new Result<JSONObject>();
        JSONObject obj = new JSONObject();
        //update-begin--Author:zhangweijian  Date:20190428 for：传入开始时间，结束时间参数
        // 获取一天的开始和结束时间
        Calendar calendar = new GregorianCalendar();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date dayStart = calendar.getTime();
        calendar.add(Calendar.DATE, 1);
        Date dayEnd = calendar.getTime();
        // 获取系统访问记录
        Long totalVisitCount = logService.findTotalVisitCount();
        obj.put("totalVisitCount", totalVisitCount);
        Long todayVisitCount = logService.findTodayVisitCount(dayStart, dayEnd);
        obj.put("todayVisitCount", todayVisitCount);
        Long todayIp = logService.findTodayIp(dayStart, dayEnd);
        //update-end--Author:zhangweijian  Date:20190428 for：传入开始时间，结束时间参数
        obj.put("todayIp", todayIp);
        result.setResult(obj);
        result.success("登录成功");
        return result;
    }

    /**
     * 登陆成功选择用户当前部门
     */
    @RequestMapping(value = "/selectDepart", method = RequestMethod.PUT)
    public Result<?> selectDepart(@RequestBody SysUser user) {
        String username = user.getUsername();
        String orgCode = user.getOrgCode();
        this.sysUserService.updateUserDepart(username, orgCode);
        return Result.ok();
    }

    @RequestMapping(value = "/verifyToken", method = RequestMethod.GET)
    public Result<JSONObject> verifyToken(HttpServletRequest request) {
        Result<JSONObject> result = new Result<>();
        String accessToken = request.getHeader("X-Access-Token");
        String username = JwtUtil.getUsername(accessToken);
        JwtUtil.getExpiresDate(accessToken);
        LambdaQueryWrapper<SysUser> lmq = new LambdaQueryWrapper<SysUser>();
        lmq.eq(SysUser::getUsername, username).eq(SysUser::getStatus, "1").eq(SysUser::getDelFlag, "0");
        SysUser user = sysUserService.getOne(lmq);
        if (user == null) {
            result.setSuccess(false);
            result.setCode(500);
            result.setMessage("操作失败，用户不可用！！！");
            return result;
        }
        Date expiresDate = JwtUtil.getExpiresDate(accessToken);
        String token = JwtUtil.signByFixedTime(username, user.getPassword(), expiresDate);
        if (accessToken != null && !"".equals(accessToken) && accessToken.equals(token)) {
            result.setSuccess(true);
            result.setCode(200);
            result.setMessage("允许登录");
            return result;
        }
        result.setSuccess(false);
        result.setCode(500);
        result.setMessage("操作失败，不可登录！！！");
        return result;
    }

    public static String doGet(String httpurl, String token) {
        HttpURLConnection connection = null;
        InputStream is = null;
        BufferedReader br = null;
        String result = null;// 返回结果字符串
        try {
            URL url = new URL(httpurl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(15000);
            connection.setReadTimeout(60000);
            connection.setRequestProperty("X-Access-Token", token);
            connection.connect();
            if (connection.getResponseCode() == 200) {
                is = connection.getInputStream();
                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                StringBuilder sbf = new StringBuilder();
                String temp;
                while ((temp = br.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            connection.disconnect();// 关闭远程连接
        }
        return result;
    }
}
